﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Common.Logging;
using System.Globalization;
using System.Diagnostics.Contracts;

namespace Common
{
    /// <summary>
    /// Redirects output from a TextWriter to the logger (ie for tracing Linq Queries)
    /// </summary>
    public class LogProxy : TextWriter
    {
        private ILog _log;
        private LogLevel _level;

        /// <summary>
        /// Constructs a Log Proxy that redirects tot he specified log and logs at the specified level
        /// </summary>
        /// <param name="log">The log to write to</param>
        /// <param name="level">The level to write at</param>
        public LogProxy(ILog log, LogLevel level) : base(CultureInfo.InvariantCulture)
        {
            Contract.Requires(log != null);
            _log = log;
            _level = level;
        }

        /// <summary>
        /// The Text Encoding used by the Log Proxy
        /// </summary>
        /// <remarks>This is hard coded to UTF8</remarks>
        public override Encoding Encoding
        {
            get { return Encoding.UTF8; }
        }

        StringBuilder sb = new StringBuilder();

        /// <summary>
        /// Writes a character to the string buffer but does not immediately write it to the log unless it is a newline character
        /// </summary>
        /// <param name="value">The character to write</param>
        public override void Write(char value)
        {
            if ((value == 10 || value == 13) && sb.Length > 0)
            { FlushToLog(); }
            else
            { sb.Append(value); }
        }
        /// <summary>
        /// Writes a string to the string buffer and flushes it to the log
        /// </summary>
        /// <param name="value">The string to be written</param>
        public override void Write(string value)
        {
            sb.Append(value);
            FlushToLog();
        }
        /// <summary>
        /// Writes a string to the string buffer and flushes it to the log
        /// </summary>
        /// <param name="value">The string to be written</param>
        public override void WriteLine(string value)
        { Write(value); }
        /// <summary>
        /// Writes a blank line to the log
        /// </summary>
        public override void WriteLine()
        {}
        /// <summary>
        /// Flushes any text currently stored in the string buffer to the log
        /// </summary>
        private void FlushToLog()
        {
            String text = sb.ToString();
            sb.Length = 0;
            switch (_level)
            {
                case LogLevel.Debug:
                    _log.Debug(text);
                    break;
                case LogLevel.Error:
                    _log.Error(text);
                    break;
                case LogLevel.Warn:
                    _log.Warn(text);
                    break;
                case LogLevel.Info:
                    _log.Info(text);
                    break;
                case LogLevel.Fatal:
                    _log.Fatal(text);
                    break;
                case LogLevel.Trace:
                default:
                    _log.Trace(text);
                    break;
            }
        }

        [ContractInvariantMethod]
        private void InvariantMethod()
        {
            Contract.Invariant(sb != null);
            Contract.Invariant(_log != null);
        }
    }
}
